package ldh.im.fxgui.controller;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.Pagination;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import ldh.im.fxbase.dao.ImSessionDao;
import ldh.im.fxbase.dao.ImUserDao;
import ldh.im.fxbase.data.dto.ImFileSearch;
import ldh.im.fxbase.data.dto.PageResult;
import ldh.im.fxbase.data.pojo.ImFile;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.data.pojo.ImUser;
import ldh.im.fxbase.service.FileService;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.component.cell.FileView;
import ldh.im.fxgui.util.ImageUtil;
import ldh.im.fxgui.util.UiUtil;
import ldh.im.fxgui.util.UserHeadImageUtil;
import ldh.im.util.DateUtil;
import ldh.rpc.util.JsonUtil;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
import org.controlsfx.control.MaskerPane;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 主页-》文件
 * 文件管理
 */
public class FileController implements Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileController.class);
    private static final double PICTURE_HEIGHT = 150;
    private static final double PICTURE_WIDTH = 150;

    @FXML private Pane centerPane;
    @FXML private Pane contentPane;
    @FXML private Pane searchResultPane; // 搜索结果

    @FXML private Pane filePane;

    @FXML private Pane friendPane;
    @FXML private Pane senderFileContentPane;

    @FXML private Pane sessionPane;
    @FXML private Pane sessionFileContentPane;

    @FXML private Pane typeFileContentPane;

    @FXML private Label ownerLabel;
    @FXML private TextField searchFileTextField;

    @FXML private GridView pictureGridView;
    @FXML private Pagination picturePagination;
    @FXML private MaskerPane pictureMaskerPane;

    private int pictureSearchPageIndex = 0;

    // 全部， 最近使用
    private ObservableList<ImFile> fileDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImFile, ?> fileVirtualFlow;

    // 发送者
    private ObservableList<ImUser> senderDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImUser, ?> senderVirtualFlow;

    private ObservableList<ImFile> senderFileDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImFile, ?> senderFileVirtualFlow;

    // 聊天
    private ObservableList<ImSession> sessionDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImSession, ?> sessionVirtualFlow;

    private ObservableList<ImFile> sessionFileDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImFile, ?> sessionFileVirtualFlow;

    // 类型
    private ObservableList<ImFile> typeFileDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImFile, ?> typeFileVirtualFlow;

    // 搜索
    private ObservableList<ImFile> searchFileDatas = FXCollections.observableArrayList();
    private VirtualFlow<ImFile, ?> searchFileVirtualFlow;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        fileVirtualFlow = VirtualFlow.createVertical(fileDatas, m->{
            FileView fileView = new FileView(m);
            return Cell.wrapNode(fileView);
        });

        VirtualizedScrollPane virtualizedScrollPane = new VirtualizedScrollPane<>(fileVirtualFlow);
        filePane.getChildren().setAll(virtualizedScrollPane);

        // 初始化发送者面板
        initSenderPane();
        // 初始化用户面板
        initSessionPane();

        initTypePane();
        // 搜索结果面板
        initSearchPane();

        initPicturePane();

        loadData(new ImFileSearch(), imFiles->{
            fileDatas.clear();
            fileDatas.addAll(imFiles);
            if (imFiles.size() > 0) {
                fileVirtualFlow.show(0);
            }
        });

        showPane("#filePane");

        String userId = UiUtil.getLoginImUser().getUserId();
        ImUser imUser = ImCacheFactory.getInstance().getUser(userId);
        ImageView headImageView = UserHeadImageUtil.createUserImage(imUser);
        headImageView.setFitHeight(40);
        headImageView.setFitWidth(40);
        ownerLabel.setGraphic(headImageView);
        ownerLabel.setText(imUser.getUserName());
        ImFileSearch imSearch = new ImFileSearch();
        imSearch.setUserId(userId);
        ownerLabel.setOnMouseClicked(e->loadData(imSearch, imFiles->{
            senderFileDatas.clear();
            senderFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                senderFileVirtualFlow.show(0);
            }
        }));

        searchFileTextField.textProperty().addListener(l->{
            String search = searchFileTextField.getText().trim();
            if (search.equals("")) {
                contentPane.setVisible(true);
                searchResultPane.setVisible(false);
                loadAllFilesAct(null);
            } else {
                contentPane.setVisible(false);
                searchResultPane.setVisible(true);

                searchFile(search);
            }
        });

        contentPane.setVisible(true);
        searchResultPane.setVisible(false);
    }

    @FXML
    private void loadAllFilesAct(ActionEvent actionEvent) {
        showPane("#filePane");
        loadData(new ImFileSearch(), imFiles -> {
            fileDatas.clear();
            fileDatas.addAll(imFiles);
            if (imFiles.size() > 0) {
                fileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void loadLastedFilesAct(ActionEvent actionEvent) {
        showPane("#filePane");
        ImFileSearch imFileSearch = new ImFileSearch();
        imFileSearch.setStartTime(DateUtil.toDate(LocalDateTime.now().plusDays(-31)));
        loadData(imFileSearch, imFiles -> {
            fileDatas.clear();
            fileDatas.addAll(imFiles);
            if (imFiles.size() > 0) {
                fileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void loadSenderFilesAct(ActionEvent actionEvent) {
        showPane("#senderPane");
        String loginUserId = UiUtil.getLoginImUser().getUserId();
        Task<List<ImUser>> senderTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ImFileSearch search = new ImFileSearch();
                search.setSize(5000);

                ImUserDao imUserDao = BeanFactory.getInstance().getBean(ImUserDao.class);
                FileService fileService = BeanFactory.getInstance().getBean(FileService.class);

                List<ImFile> imFiles = fileService.search(loginUserId, search);
                Set<String> userIds = imFiles.stream().map(ImFile::getUserId).collect(Collectors.toSet());
                List<ImUser> imUsers = imUserDao.getUserByIds(userIds.stream().collect(Collectors.toList()));
                imUsers.remove(UiUtil.getLoginImUser());
                return imUsers;
            }
        };
        ThreadPoolUtil.submitTask(senderTask);
        senderTask.setOnSucceeded(s->{
            senderDatas.setAll(senderTask.getValue());
        });
    }

    @FXML
    private void loadSessionAct(ActionEvent actionEvent) {
        showPane("#sessionPane");
        String loginUserId = UiUtil.getLoginImUser().getUserId();
        Task<List<ImSession>> sessionTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ImFileSearch search = new ImFileSearch();
                search.setSize(5000);

                ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
                FileService fileService = BeanFactory.getInstance().getBean(FileService.class);

                List<ImFile> imFiles = fileService.search(loginUserId, search);
                List<String> serverIds = imFiles.stream().map(ImFile::getSessionId).collect(Collectors.toList());
                List<ImSession> imSessions = imSessionDao.getSessionByServerIds(loginUserId, serverIds);
                return imSessions;
            }
        };
        ThreadPoolUtil.submitTask(sessionTask);
        sessionTask.setOnSucceeded(s->{
            sessionDatas.setAll(sessionTask.getValue());
        });
    }

    @FXML
    private void loadTypeAct(ActionEvent actionEvent) {
        showPane("#typePane");
    }

    @FXML
    private void loadPictureAct(ActionEvent actionEvent) {
        showPane("#picturePane");
        String loginUserId = UiUtil.getLoginImUser().getUserId();
        Task<PageResult<ImFile>> fileTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ImFileSearch search = new ImFileSearch();
                search.setSuffix(Arrays.stream(FileUtil.IMAGE_SUFFIXS.split(",")).filter(s->!s.equals(",")).collect(Collectors.toList()));
                search.setSize(8);
                search.setStart(pictureSearchPageIndex * 8);

                FileService fileService = BeanFactory.getInstance().getBean(FileService.class);
                PageResult<ImFile> imFiles = fileService.searchForPage(loginUserId, search);
                return imFiles;
            }
        };
        ThreadPoolUtil.submitTask(fileTask);
        fileTask.setOnSucceeded(s->{
            pictureGridView.getItems().setAll(fileTask.getValue().getDatas());
            picturePagination.setPageCount(fileTask.getValue().getTotal()/8);
        });
        fileTask.runningProperty().addListener(l->{
            pictureMaskerPane.setVisible(fileTask.isRunning());
        });
    }

    @FXML
    private void searchDoc(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.DOC_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchExcel(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.EXCEL_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchPpt(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.PPT_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchZip(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.ZIP_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchAudio(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.AUDIO_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchExe(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        search.setSuffix(Arrays.stream(FileUtil.EXE_SUFFIXS.split(",")).collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                typeFileVirtualFlow.show(0);
            }
        });
    }

    @FXML
    private void searchOther(ActionEvent actionEvent) {
        ImFileSearch search = new ImFileSearch();
        String[] strs = new String[] {FileUtil.DOC_SUFFIXS, FileUtil.EXCEL_SUFFIXS, FileUtil.PPT_SUFFIXS,
                FileUtil.ZIP_SUFFIXS, FileUtil.AUDIO_SUFFIXS, FileUtil.EXE_SUFFIXS};
        Set<String> suffixes = new HashSet<>();
        for (String str : strs) {
            suffixes.addAll(Arrays.stream(str.split(",")).collect(Collectors.toList()));
        }
        search.setNoSuffix(suffixes.stream().collect(Collectors.toList()));
        loadData(search, imFiles -> {
            typeFileDatas.setAll(imFiles);
            if (imFiles.size() > 0) {
                searchFileVirtualFlow.show(0);
            }
        });
    }

    private void loadData(ImFileSearch search, Consumer<List<ImFile>> consumer) {
        fileDatas.clear();
        String loginUserId = UiUtil.getLoginImUser().getUserId();
        Task<List<ImFile>> loadTask = new Task() {

            @Override
            protected Object call() throws Exception {
                FileService fileService = BeanFactory.getInstance().getBean(FileService.class);
                return fileService.search(loginUserId, search);
            }
        };
        ThreadPoolUtil.submitTask(loadTask);
        loadTask.setOnSucceeded(s->{
            consumer.accept(loadTask.getValue());
        });
    }

    private void searchFile(String search) {
        ImFileSearch imFileSearch = new ImFileSearch();
        imFileSearch.setSearch(search);
        loadData(imFileSearch, imFiles->{
            searchFileDatas.clear();
            searchFileDatas.addAll(imFiles);
            if (imFiles.size() > 0) {
                searchFileVirtualFlow.show(0);
            }
        });
    }

    private void showPane(String paneId) {
        centerPane.getChildren().forEach(node->node.setVisible(false));
        Node node = centerPane.lookup(paneId);
        if (node != null) node.setVisible(true);
    }

    private void initSenderPane() {
        senderVirtualFlow = VirtualFlow.createVertical(senderDatas, m->{
            Label label = new Label();
            label.getStyleClass().add("sender-item-view");
            ImageView imageView = UserHeadImageUtil.createUserImage(m);
            imageView.setFitHeight(40);
            imageView.setFitWidth(40);
            label.setGraphic(imageView);
            label.setText(m.getUserName());
            label.setContentDisplay(ContentDisplay.LEFT);

            ImFileSearch search = new ImFileSearch();
            search.setUserId(m.getUserId());
            label.setOnMouseClicked(e->loadData(search, imFiles->{
                senderFileDatas.clear();
                senderFileDatas.setAll(imFiles);
                if (imFiles.size() > 0) {
                    senderFileVirtualFlow.show(0);
                }
            }));
            return Cell.wrapNode(label);
        });
        VirtualizedScrollPane virtualizedScrollPane = new VirtualizedScrollPane<>(senderVirtualFlow);
        friendPane.getChildren().setAll(virtualizedScrollPane);


        senderFileVirtualFlow = VirtualFlow.createVertical(senderFileDatas, m->{
            FileView fileView = new FileView(m);
            return Cell.wrapNode(fileView);
        });

        VirtualizedScrollPane virtualizedScrollPane2 = new VirtualizedScrollPane<>(senderFileVirtualFlow);
        senderFileContentPane.getChildren().setAll(virtualizedScrollPane2);
    }

    private void initSessionPane() {
        sessionVirtualFlow = VirtualFlow.createVertical(sessionDatas, m->{
            Label label = new Label();
            label.getStyleClass().add("session-item-view");
            Node node = UserHeadImageUtil.createSessionImage(m);
            if (node instanceof ImageView) {
                ImageView imageView = (ImageView) node;
                imageView.setFitWidth(40);
                imageView.setFitHeight(40);
            } else if (node instanceof Pane) {
                ((Pane)node).setPrefSize(40, 40);
            }

            label.setGraphic(node);
            label.setText(m.getName());
            label.setContentDisplay(ContentDisplay.LEFT);

            ImFileSearch search = new ImFileSearch();
            search.setSessionId(m.getServerId());
            label.setOnMouseClicked(e->loadData(search, imFiles -> {
                sessionFileDatas.clear();
                sessionFileDatas.setAll(imFiles);
                if (imFiles.size() > 0) {
                    sessionFileVirtualFlow.show(0);
                }
            }));
            return Cell.wrapNode(label);
        });
        VirtualizedScrollPane virtualizedScrollPane = new VirtualizedScrollPane<>(sessionVirtualFlow);
        sessionPane.getChildren().setAll(virtualizedScrollPane);

        sessionFileVirtualFlow = VirtualFlow.createVertical(sessionFileDatas, m->{
            FileView fileView = new FileView(m);
            return Cell.wrapNode(fileView);
        });

        VirtualizedScrollPane virtualizedScrollPane2 = new VirtualizedScrollPane<>(sessionFileVirtualFlow);
        sessionFileContentPane.getChildren().setAll(virtualizedScrollPane2);
    }

    private void initTypePane() {
        typeFileVirtualFlow = VirtualFlow.createVertical(typeFileDatas, m->{
            FileView fileView = new FileView(m);
            return Cell.wrapNode(fileView);
        });

        VirtualizedScrollPane virtualizedScrollPane2 = new VirtualizedScrollPane<>(typeFileVirtualFlow);
        typeFileContentPane.getChildren().setAll(virtualizedScrollPane2);
    }

    private void initSearchPane() {
        searchFileVirtualFlow = VirtualFlow.createVertical(searchFileDatas, m->{
            FileView fileView = new FileView(m);
            return Cell.wrapNode(fileView);
        });

        VirtualizedScrollPane virtualizedScrollPane2 = new VirtualizedScrollPane<>(searchFileVirtualFlow);
        searchResultPane.getChildren().setAll(virtualizedScrollPane2);
        contentPane.setVisible(false);
    }

    private void initPicturePane() {
        pictureGridView.setCellWidth(150);
        pictureGridView.setCellHeight(150);
        pictureGridView.setCellFactory(e->{
            return new GridCell<ImFile>() {
                @Override
                protected void updateItem(ImFile imFile, boolean empty) {
                    String filePath = "";
                    try {
                        super.updateItem(imFile, empty);
                        if (imFile == null) return;
                        filePath = FileUtil.getFilePath(imFile);
                        if (filePath == null) {
                            LOGGER.error("附件路径为空：{}, {}", filePath, JsonUtil.toJson(imFile));
                            return;
                        }

                        ImageView imageView = new ImageView();
                        if (ImageUtil.isGif(filePath)) {
                            Image image = ImageUtil.getFirstGifImage(filePath, true);
                            setImage(imageView, image);
                        } else {
                            Image image = ImageUtil.getCacheImage(filePath, true);
                            setImage(imageView, image);
                        }

                        StackPane stackPane = new StackPane(imageView);
                        stackPane.getStyleClass().add("image-item-view");
                        this.setGraphic(stackPane);
                        return;
                    } catch (Exception e) {
                        LOGGER.error("错误数据：{}, {}", filePath, JsonUtil.toJson(imFile));
                        LOGGER.error("文件路径不对", e);
                    }
                    super.updateItem(null, empty);
                    setGraphic(null);
                    setText(null);
                }
            };
        });

        picturePagination.currentPageIndexProperty().addListener((l)->{
            pictureSearchPageIndex = picturePagination.getCurrentPageIndex();
            loadPictureAct(null);
        });

        pictureMaskerPane.setText("数据加载中...");
    }

    private void setImage(ImageView imageView, Image image) {
        imageView.setImage(image);
        if(image.getHeight() > image.getWidth()) {
            if (image.getHeight() > PICTURE_HEIGHT) {
                imageView.setPreserveRatio(true);
                imageView.setFitHeight(PICTURE_HEIGHT);
            }
        } else {
            if (image.getWidth() > PICTURE_WIDTH) {
                imageView.setPreserveRatio(true);
                imageView.setFitWidth(PICTURE_WIDTH);
            }
        }

        imageView.setOnMouseClicked(e->{
            if (e.getClickCount() != 2) return;
            // todo 增加图片展示
        });
    }

}
